#!/usr/bin/env python

"""
Access to QM1538

Copyright (C) 2010 Thomas Schulz, t.schulz3016@gmail.com

"""


__version__ = "$Id:$"


import serial

import os, sys
import struct


lookuptable = {0x7d: 0,
               0x05: 1,
               0x5b: 2,
               0x1f: 3,
               0x27: 4,
               0x3e: 5,
               0x7e: 6,
               0x15: 7,
               0x7f: 8,
               0x3f: 9}


class QM1538:
    serPort = None
    
    def __init__(self, port=None):
        """Initialize the object.
        If called with None: do not open the serial port. This
        is useful to feed in data from a string or file.
        """
        
        if port != None:
            self.Open(port)

    def __del__(self):
        self.serPort.close()
        

    def Open(self, port=0):
        """Open the serial port to the DMM"""
        
        self.serPort = serial.Serial(port, 2400)
        

    def Close(self):
        """Close the serial port."""
        
        self.serPort.close()
        self.serPort = None


    def Validate14Bytes(self, raw):
        
        # Check for valid data length
        if len(raw) != 14:
            print "QM1538.Proces14Bytes data length error: %d" % len(raw)
            return

        # Build a tuple of byte values
        m = struct.unpack("B"*14, raw)

        # Check the four msb's of each byte
        for i in range(len(m)):
            if i+1 != ((m[i] & 0xf0) >> 4):
                print "QM1538.Process14Bytes data error", i, hex(m[i])
                return False
        return True
        

    def Process14Bytes(self, raw):
        
        # Check for valid data length
        if len(raw) != 14:
            print "QM1538.Proces14Bytes data length error: %d" % len(raw)
            return

        # Build a tuple of byte values
        m = struct.unpack("B"*14, raw)

        # Check the four msb's of each byte
        for i in range(len(m)):
            if i+1 != ((m[i] & 0xf0) >> 4):
                print "QM1538.Process14Bytes data error", i, hex(m[i])
        
        #for i in range(len(m)):
        #    print hex(m[i]),
        #print

        # Put digits together
        d1 = ((m[1] & 0x7) << 4) | (m[2] & 0xf)
        d2 = ((m[3] & 0x7) << 4) | (m[4] & 0xf)
        d3 = ((m[5] & 0x7) << 4) | (m[6] & 0xf)
        d4 = ((m[7] & 0x7) << 4) | (m[8] & 0xf)
        #print hex(d1), hex(d2), hex(d3), hex(d4)

        # Sign
        if (m[1] & 0x8):
            sign = -1.0
        else:
            sign = 1.0

        # Decimal point
        if (m[3] & 0x8):
            decimals = 0.001
        elif (m[5] & 0x8):
            decimals = 0.01
        elif (m[7] & 0x8):
            decimals = 0.1
        else:
            decimals = 1

        # Convert the 7-Segment code into a number
        v1 = lookuptable[d1]
        v2 = lookuptable[d2]
        v3 = lookuptable[d3]
        v4 = lookuptable[d4]
        #print v1, v2, v3, v4, sign

        value = (v1*1000 + v2*100 + v3*10 + v4) * sign * decimals
        return value


    def ReadValue(self):
        """Read one value from the DMM. Return a float value."""
        
        data = self.serPort.read(14)
        return self.Process14Bytes(data)
        

def TestDatafile():
    """Read a datafile and feed into DMM object"""
    
    filename = "sampleData\dmm.capture.TXT"

    f = file(filename, "r")
    dmm = QM1538(None)
    while True:
        raw = f.read(14)
        if len(raw) != 14:
            break
        value = dmm.Process14Bytes(raw)    
        print value


def TestSerial():
    """Read data from serial port"""
    
    dmm = QM1538(0)
    while True:
        value = dmm.ReadValue()
        print value
        
        
def main(sys_argv):

    #TestDatafile()
    TestSerial()
    

if __name__ == "__main__":
    main(sys.argv)
